package com.jzo2o.orders.manager.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jzo2o.api.customer.AddressBookApi;
import com.jzo2o.api.customer.dto.response.AddressBookResDTO;
import com.jzo2o.api.foundations.ServeApi;
import com.jzo2o.api.foundations.dto.response.ServeAggregationResDTO;
import com.jzo2o.api.orders.dto.response.OrderResDTO;
import com.jzo2o.api.orders.dto.response.OrderSimpleResDTO;
import com.jzo2o.api.trade.RefundRecordApi;
import com.jzo2o.api.trade.TradingApi;
import com.jzo2o.api.trade.dto.response.ExecutionResultResDTO;
import com.jzo2o.api.trade.dto.response.TradingResDTO;
import com.jzo2o.common.constants.UserType;
import com.jzo2o.common.enums.EnableStatusEnum;
import com.jzo2o.common.expcetions.CommonException;
import com.jzo2o.common.utils.BeanUtils;
import com.jzo2o.common.utils.DateUtils;
import com.jzo2o.common.utils.ObjectUtils;
import com.jzo2o.mvc.utils.UserContext;
import com.jzo2o.orders.base.config.OrderStateMachine;
import com.jzo2o.orders.base.enums.OrderPayStatusEnum;
import com.jzo2o.orders.base.enums.OrderRefundStatusEnum;
import com.jzo2o.orders.base.enums.OrderStatusEnum;
import com.jzo2o.orders.base.mapper.OrdersMapper;
import com.jzo2o.orders.base.model.domain.Orders;
import com.jzo2o.orders.base.model.domain.OrdersCanceled;
import com.jzo2o.orders.base.model.domain.OrdersRefund;
import com.jzo2o.orders.base.model.dto.OrderSnapshotDTO;
import com.jzo2o.orders.base.model.dto.OrderUpdateStatusDTO;
import com.jzo2o.orders.base.service.IOrdersCommonService;
import com.jzo2o.orders.manager.handler.OrderHandler;
import com.jzo2o.orders.manager.model.dto.OrderCancelDTO;
import com.jzo2o.orders.manager.model.dto.request.PlaceOrderReqDTO;
import com.jzo2o.orders.manager.model.dto.response.PlaceOrderResDTO;
import com.jzo2o.orders.manager.service.IOrdersCanceledService;
import com.jzo2o.orders.manager.service.IOrdersManagerService;
import com.jzo2o.orders.manager.service.IOrdersRefundService;
import com.jzo2o.redis.helper.CacheHelper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;

import static com.jzo2o.orders.base.constants.FieldConstants.SORT_BY;
import static com.jzo2o.orders.base.constants.RedisConstants.Lock.ORDERS_SHARD_KEY_ID_GENERATOR;

/**
 * <p>
 * 订单表 服务实现类
 * </p>
 *
 * @author itcast
 * @since 2023-07-10
 */
@Slf4j
@Service
public class OrdersManagerServiceImpl extends ServiceImpl<OrdersMapper, Orders> implements IOrdersManagerService {
    @Resource
    private ServeApi serveApi;
    @Resource
    private AddressBookApi addressBookApi;
    @Resource
    private RedisTemplate redisTemplate;
    @Resource
    private IOrdersCommonService ordersCommonService;
    @Resource
    private IOrdersCanceledService ordersCanceledService;
    @Resource
    private OrdersManagerServiceImpl impl;
    @Resource
    private TradingApi tradingApi;
    @Resource
    private RefundRecordApi refundRecordApi;
    @Resource
    private IOrdersRefundService ordersRefundService;
    @Resource
    private OrderHandler orderHandler;
    @Resource
    private OrderStateMachine orderStateMachine;
    @Resource
    private CacheHelper cacheHelper;

    @Override
    public List<Orders> batchQuery(List<Long> ids) {
        LambdaQueryWrapper<Orders> queryWrapper = Wrappers.<Orders>lambdaQuery().in(Orders::getId, ids).ge(Orders::getUserId, 0);
        return baseMapper.selectList(queryWrapper);
    }

    @Override
    public Orders queryById(Long id) {
        return baseMapper.selectById(id);
    }

    /**
     * 滚动分页查询
     *
     * @param currentUserId 当前用户id
     * @param ordersStatus  订单状态，0：待支付，100：派单中，200：待服务，300：服务中，400：待评价，500：订单完成，600：已取消，700：已关闭
     * @param sortBy        排序字段
     * @return 订单列表
     */
    @Override
    public List<OrderSimpleResDTO> consumerQueryList(Long currentUserId, Integer ordersStatus, Long sortBy) {
        //1.构件查询条件
        LambdaQueryWrapper<Orders> queryWrapper = Wrappers.<Orders>lambdaQuery()
                .eq(ObjectUtils.isNotNull(ordersStatus), Orders::getOrdersStatus, ordersStatus)
                .lt(ObjectUtils.isNotNull(sortBy), Orders::getSortBy, sortBy)
                .eq(Orders::getUserId, currentUserId)
                .eq(Orders::getDisplay, EnableStatusEnum.ENABLE.getStatus());
        Page<Orders> queryPage = new Page<>();
        queryPage.addOrder(OrderItem.desc(SORT_BY));
        queryPage.setSearchCount(false);

        //2.查询订单列表
        Page<Orders> ordersPage = baseMapper.selectPage(queryPage, queryWrapper);
        List<Orders> records = ordersPage.getRecords();

        List<OrderSimpleResDTO> orderSimpleResDTOS = BeanUtil.copyToList(records, OrderSimpleResDTO.class);
        return orderSimpleResDTOS;

    }
    /**
     * 根据订单id查询
     *
     * @param id 订单id
     * @return 订单详情
     */
    @Override
    public OrderResDTO getDetail(Long id) {
        Orders orders = queryById(id);
        // 对订单进行操作，将已支付超时的订单状态进行操作，重新返回
        orders = impl.canalIfPayOvertime(orders);
        OrderResDTO orderResDTO = BeanUtil.toBean(orders, OrderResDTO.class);
        return orderResDTO;
    }

    /**
     * 订单评价
     *
     * @param ordersId 订单id
     */
    @Override
    @Transactional
    public void evaluationOrder(Long ordersId) {
//        //查询订单详情
//        Orders orders = queryById(ordersId);
//
//        //构建订单快照
//        OrderSnapshotDTO orderSnapshotDTO = OrderSnapshotDTO.builder()
//                .evaluationTime(LocalDateTime.now())
//                .build();
//
//        //订单状态变更
//        orderStateMachine.changeStatus(orders.getUserId(), orders.getId().toString(), OrderStatusChangeEventEnum.EVALUATE, orderSnapshotDTO);
    }

    /**
     * 用户下单接口
     *
     * @param dto     下单数据
     * @param request request
     * @return Long
     */
    @Transactional
    @Override
    public PlaceOrderResDTO place(PlaceOrderReqDTO dto, HttpServletRequest request) {
        // 服务信息
        ServeAggregationResDTO serveInfo = serveApi.getServeInfo(dto.getServeId());
        // 地址薄信息
        AddressBookResDTO addressBookInfo = addressBookApi.getAddressBookInfo(dto.getAddressBookId());
        // 封装数据
        Orders orders = new Orders();
        orders.setId(generateOrderId());
        orders.setUserId(UserContext.currentUserId());
        orders.setServeTypeId(serveInfo.getServeTypeId());
        orders.setServeTypeName(serveInfo.getServeTypeName());
        orders.setServeItemId(serveInfo.getServeItemId());
        orders.setServeItemName(serveInfo.getServeItemName());
        orders.setServeItemImg(serveInfo.getServeItemImg());
        orders.setUnit(serveInfo.getUnit());
        orders.setServeId(serveInfo.getId());
        orders.setOrdersStatus(0);
        orders.setPayStatus(OrderPayStatusEnum.NO_PAY.getStatus());
        orders.setServeStartTime(dto.getServeStartTime());
        orders.setPrice(serveInfo.getPrice());
        orders.setPurNum(dto.getPurNum());
        BigDecimal totalAmount = serveInfo.getPrice().multiply(new BigDecimal(dto.getPurNum()));
        orders.setTotalAmount(totalAmount);
        orders.setDiscountAmount(new BigDecimal("0"));
        orders.setRealPayAmount(totalAmount.subtract(orders.getDiscountAmount()));
        orders.setCityCode(serveInfo.getCityCode());
        orders.setServeAddress(addressBookInfo.getAddress());
        orders.setContactsPhone(addressBookInfo.getPhone());
        orders.setContactsName(addressBookInfo.getName());
        orders.setLon(addressBookInfo.getLon());
        orders.setLat(addressBookInfo.getLat());
        // 服务时间
        long sortBy = DateUtils.toEpochMilli(orders.getServeStartTime()) + orders.getId() % 100000;
        orders.setSortBy(sortBy);

        boolean flag = save(orders);
        // 开启状态机，生成订单快照
        OrderSnapshotDTO orderSnapshotDTO =
                BeanUtils.toBean(baseMapper.selectById(orders.getId()), OrderSnapshotDTO.class);
        orderStateMachine.start(null, orders.getId().toString(), orderSnapshotDTO);
        if(!flag) {
            throw new CommonException("创建订单失败，请重新下单");
        }
        return new PlaceOrderResDTO(orders.getId());
    }

    /**
     * 取消订单
     * @param orderCancelDTO 取消订单模型
     */
    @Override
    public void cancel(OrderCancelDTO orderCancelDTO) {
        Long orderId = orderCancelDTO.getId();
        // 判断订单是否存在
        Orders orders = getById(orderId);
        if(ObjectUtils.isNull(orders)) {
            throw new CommonException("您所取消的订单不存在，请联系客服");
        }

        Integer ordersStatus = orders.getOrdersStatus();
        // 判断订单状态 待支付/已支付
        if(ObjectUtils.equal(ordersStatus, OrderStatusEnum.NO_PAY.getStatus())) {
            // 待支付
            impl.cancelByNoPay(orderCancelDTO);
        } else if(ObjectUtils.equal(ordersStatus, OrderStatusEnum.DISPATCHING.getStatus())) {
            // 已支付
            orderCancelDTO.setTradingOrderNo(orders.getTradingOrderNo());
            orderCancelDTO.setRealPayAmount(orders.getRealPayAmount());
            impl.cancelByDispatching(orderCancelDTO);
            // 开启新的线程及时退款，将退款表中已写入的待退款数据，去除进行退款
            new Thread(() -> {
                ExecutionResultResDTO executionResultResDTO = null;
                // 待退款信息
                OrdersRefund ordersRefund = ordersRefundService.getById(orders.getId());
                try {
                    // 请求微信接口进行退款
                    executionResultResDTO = refundRecordApi.refundTrading(ordersRefund.getTradingOrderNo(), ordersRefund.getRealPayAmount());
                } catch (Exception e) {
                    e.printStackTrace();
                }
                if(ObjectUtils.isNotNull(executionResultResDTO)) {
                    // 修改订单退款信息,如果退款成功将待退款信息删除
                    //orderHandler.updateRefundOrder(ordersRefund, executionResultResDTO);
                }
            }).start();
        }
    }

    /**
     * 开启新的线程及时退款，将退款表中已写入的待退款数据，去除进行退款
     * @param id
     */
    /*private void requestRefundNewThread(Long id) {
        new Thread(() -> {
            ExecutionResultResDTO executionResultResDTO = null;
            // 待退款信息
            OrdersRefund ordersRefund = ordersRefundService.getById(id);
            try {
                // 请求微信接口进行退款
                executionResultResDTO = refundRecordApi.refundTrading(ordersRefund.getTradingOrderNo(), ordersRefund.getRealPayAmount());
            } catch (Exception e) {
                e.printStackTrace();
            }
            if(ObjectUtils.isNotNull(executionResultResDTO)) {
                // 修改订单退款信息,如果退款成功将待退款信息删除
                orderHandler.updateRefundOrder(ordersRefund, executionResultResDTO);
            }
        }).start();
    }*/

    /**
     * 取消已支付(分配中)的订单
     */
    @Transactional(rollbackFor = Exception.class)
    public void cancelByDispatching(OrderCancelDTO dto) {
        // 保存取消订单记录
        OrdersCanceled ordersCanceled = BeanUtils.toBean(dto, OrdersCanceled.class);
        ordersCanceled.setCancellerId(dto.getCurrentUserId());
        ordersCanceled.setCancelerName(dto.getCurrentUserName());
        ordersCanceled.setCancellerType(UserType.C_USER);
        ordersCanceled.setCancelTime(LocalDateTime.now());
        ordersCanceled.setCreateTime(LocalDateTime.now());
        ordersCanceledService.save(ordersCanceled);

        // 修改订单状态
        OrderUpdateStatusDTO orderUpdateStatusDTO = OrderUpdateStatusDTO.builder().id(dto.getId())
                .originStatus(OrderStatusEnum.DISPATCHING.getStatus())
                .targetStatus(OrderStatusEnum.CLOSED.getStatus())
                .refundStatus(OrderRefundStatusEnum.REFUNDING.getStatus())//退款状态为退款中
                .build();
        ordersCommonService.updateStatus(orderUpdateStatusDTO);

        // 报错待退款的订单记录
        OrdersRefund ordersRefund = OrdersRefund.builder().id(dto.getId())
                .tradingOrderNo(dto.getTradingOrderNo())
                .realPayAmount(dto.getRealPayAmount())
                .build();
        ordersRefundService.save(ordersRefund);
    }

    /**
     * 如果支付过期则取消订单(懒加载方式)
     * @param orders
     */
    private Orders canalIfPayOvertime(Orders orders){
        // 订单状态是未支付才可以取消订单
        if(orders.getOrdersStatus() == OrderStatusEnum.NO_PAY.getStatus()
            && LocalDateTime.now().minusMinutes(15).isBefore(orders.getCreateTime())) {
            // 取消订单前再查询订单支付的状态，未支付才能取消订单
            Integer payStatus = orders.getPayStatus();
            TradingResDTO dto = tradingApi.findTradResultByTradingOrderNo(orders.getTradingOrderNo());
            // 判断
            if(!Objects.equals(payStatus, dto.getTradingState().getCode())) {
                OrderCancelDTO orderCancelDTO = BeanUtils.toBean(orders, OrderCancelDTO.class);
                orderCancelDTO.setCurrentUserType(UserType.SYSTEM);
                orderCancelDTO.setCancelReason("支付超时，自动取消订单");
                impl.cancelByNoPay(orderCancelDTO);

            }
        }
        return getById(orders.getId());
    }

    /**
     * 取消待支付订单
     * @param dto 订单信息
     */
    @Transactional
    public void cancelByNoPay(OrderCancelDTO dto) {
        // 保存取消订单记录
        OrdersCanceled ordersCanceled = BeanUtils.toBean(dto, OrdersCanceled.class);
        ordersCanceled.setCancellerId(dto.getCurrentUserId());
        ordersCanceled.setCancelerName(dto.getCurrentUserName());
        ordersCanceled.setCancellerType(UserType.C_USER);
        ordersCanceled.setCancelTime(LocalDateTime.now());
        ordersCanceled.setCreateTime(LocalDateTime.now());
        ordersCanceledService.save(ordersCanceled);

        // 修改订单状态
        OrderUpdateStatusDTO orderUpdateStatusReqDTO = BeanUtils.toBean(dto, OrderUpdateStatusDTO.class);
        orderUpdateStatusReqDTO.setOriginStatus(OrderStatusEnum.NO_PAY.getStatus());
        orderUpdateStatusReqDTO.setTargetStatus(OrderStatusEnum.CANCELED.getStatus());

        ordersCommonService.updateStatus(orderUpdateStatusReqDTO);
    }

    /**
     * 生成订单id 格式：{yyMMdd}{13位id}
     * @return
     */
    private Long generateOrderId() {
        //通过Redis自增序列得到序号
        Long id = redisTemplate.opsForValue().increment(ORDERS_SHARD_KEY_ID_GENERATOR, 1);
        //生成订单号   2位年+2位月+2位日+13位序号
        long orderId = DateUtils.getFormatDate(LocalDateTime.now(), "yyMMdd") * 10000000000000L + id;
        return orderId;
    }

}
